<template>
  <div class="draggable-upload-group">
    <draggable
      v-model="imageList"
      :class="
        'draggable-upload-group-show-section' +
          (editDesc ? ' picture-collection ' : '')
      "
      chosen-class="chosen"
      force-fallback="true"
      :group="group"
      animation="1000"
      handle=".image-unit"
      @start="handleImageOnStart"
      @end="handleImageOnEnd"
    >
      <transition-group class="draggable-upload-group-transition-section">
        <div
          v-for="(item, index) in imageList"
          :key="item.id"
          class="draggable-upload-group-show-list"
        >
          <div class="image-unit">
            <img
              class="el-upload-list__item-thumbnail"
              :src="item.url"
              alt="item.name"
            >
            <span class="el-upload-list__item-actions">
              <span
                class="el-upload-list__item-preview"
                :title="$t('jspImageUpload.preview')"
                @click="handleImagePictureCardPreview(item, index)"
              >
                <i class="el-icon-zoom-in" />
              </span>
              <span
                class="el-upload-list__item-edit"
                :title="$t('jspImageUpload.edit')"
                @click="handleImageEdit(item, index)"
              >
                <i class="el-icon-edit" />
              </span>
              <span
                class="el-upload-list__item-delete"
                :title="$t('jspImageUpload.delete')"
                @click="handleImageRemove(item)"
              >
                <i class="el-icon-delete" />
              </span>
            </span>
          </div>
          <div class="picture-description">
            <el-input
              v-if="editDesc"
              v-model="item.description"
              type="textarea"
              :placeholder="$t('jspImageUpload.description_tips')"
              @input="handleDescriptionInput"
            />
          </div>
        </div>
      </transition-group>
    </draggable>

    <div
      :class="
        'draggable-upload-group-uploadbar' +
          (editDesc ? ' picture-collection-uploadbar ' : '')
      "
      @click="showUploadDrawer = true"
    >
      <div tabindex="0" class="el-upload el-upload--picture-card">
        <i class="el-icon-plus" />
      </div>
    </div>

    <el-drawer
      :title="
        (title || $t('jspImageUpload.upload_drawer_title')) +
          '（' +
          selected_fileList.length +
          '）'
      "
      :visible.sync="showUploadDrawer"
      direction="rtl"
      class="draggable-upload-group-upload-drawer"
      :wrapper-closable="!submitLoading"
      :before-close="handleDrawerClose"
      :modal="modal"
      size="50%"
    >
      <div class="draggable-upload-group-upload-drawer-content">
        <el-upload
          ref="thumbnailUpload"
          class="draggable-upload-group-upload-drawer-upload-bar"
          :action="upload_action"
          list-type="picture"
          :limit="limit"
          :auto-upload="false"
          :on-exceed="handleImageExceed"
          :file-list="selected_fileList"
          :show-file-list="true"
          :accept="acceptType"
          :headers="headers"
          :name="name"
          multiple
          :on-change="handleOnChange"
          :on-remove="handleOnRemove"
          :data="{ water }"
          :on-success="handleOnSuccess"
          :on-error="handleOnError"
          :disabled="submitLoading"
        >
          <el-button
            type="primary"
          ><i class="awesome-icon fas fa-tv" />
            {{ $t('jspImageUpload.choose_image') }}</el-button>
          <el-alert
            class="upload_tips"
            :title="uploadDefaultTip"
            type="info"
            show-icon
            :closable="false"
          />
        </el-upload>
        <div class="draggable-upload-group-upload-drawer-submit-section">
          <el-checkbox v-model="water" :true-label="1" :false-label="0">{{
            $t('jspImageUpload.water')
          }}</el-checkbox>
          <el-button-group>
            <el-button @click="handleDrawerCancel">{{
              $t('jspImageUpload.cancel')
            }}</el-button>
            <el-button :disabled="submitLoading" @click="handleDrawerClear">{{
              $t('jspImageUpload.clear')
            }}</el-button>
            <el-button
              type="primary"
              :loading="submitLoading"
              @click="handleUpload"
            >
              {{
                submitLoading
                  ? $t('jspImageUpload.uploading')
                  : $t('jspImageUpload.upload')
              }}</el-button>
          </el-button-group>
        </div>
      </div>
    </el-drawer>
    <!-- 图片查看器 -->
    <jsp-image-viewer
      :show="imageViewerVisible"
      :url-list="imageViewerList"
      @close="imageViewerVisible = false"
    />
    <!--图片编辑对话框-->
    <jsp-image-cropper-dialog
      :modal="modal"
      :visible="cropperVisible"
      :cropper-config="cropperConfig"
      :is-upload="cropperIsUploading"
      :origin-file="cropperOriginFile"
      @close="cropperVisible = false"
      @cut="handleCropperCut"
      @upload="handleCropperUpload"
    />
  </div>
</template>

<script>
import JspImageViewer from '@/components/JspImageViewer'
import draggable from 'vuedraggable'
import JspImageCropperDialog from '@/components/JspImageCropperDialog'
import request from '@/utils/request'

export default {
  name: 'JspImageUpload',
  components: {
    JspImageViewer,
    draggable,
    JspImageCropperDialog
  },
  props: {
    // 组件中文名称
    title: {
      type: String,
      default: ''
    },
    // 默认图片数据列表
    defaultList: {
      type: Array,
      default: () => {
        return []
      }
    },
    // 上传地址
    action: {
      type: String,
      default: ''
    },
    // 上传类型：不填写默认所有
    // 格式：.jpg,.png,.gif,.bmp等
    accept: {
      type: String,
      default: '.gif,.jpg,.png,.bmp'
    },
    // 上传数量限制
    limit: {
      type: Number,
      default: 1
    },
    // 上传大小限制，默认2M
    size: {
      type: Number,
      default: 2
    },
    // 请求头设置
    headers: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 上传字段名称
    name: {
      type: String,
      default: 'file'
    },
    // 分组（组之间可进行拖拽）
    group: {
      type: String,
      default: ''
    },
    // 可编辑描述
    editDesc: {
      type: Boolean,
      default: false
    },
    // 图片宽度：上传尺寸提示、图片裁剪组件autoCropWidth参数
    width: {
      type: Number,
      default: 0
    },
    // 图片高度：上传尺寸提示、图片裁剪组件autoCropHeight参数
    height: {
      type: Number,
      default: 0
    },
    // 上传要求提示信息
    uploadTip: {
      type: String,
      default: ''
    },
    // 是否遮罩
    modal: {
      type: Boolean,
      default: true
    }
  },
  data() {
    const uploadDefaultTip =
      this.$t('jspImageUpload.upload_tips', {
        type: this.accept,
        limit: this.limit,
        size: this.size
      }) +
      (this.width && this.height
        ? '，' +
          this.$t('jspImageUpload.limit_w_h') +
          '：' +
          this.width +
          'x' +
          this.height +
          this.$t('jspImageUpload.px')
        : '')
    return {
      submitLoading: false,
      showUploadDrawer: false,
      imageViewerVisible: false,
      imageViewerList: [],
      imageList: this.defaultList,
      acceptType: this.accept,
      water: 0,
      upload_action: this.action,
      selected_fileList: [],
      success_uploaded_num: 0,
      error_uploaded_num: 0,
      success_response: null,
      error_response: null,
      cropperVisible: false,
      cropperIsUploading: true,
      cropperOriginFile: {}, // 裁剪所需源文件信息
      cropperConfig: {},
      uploadDefaultTip: this.uploadTip || uploadDefaultTip
    }
  },
  watch: {
    defaultList() {
      this.imageList = this.defaultList
    }
  },
  methods: {
    // 移除图片
    handleImageRemove(file) {
      this.imageList = this.imageList.filter((currentValue) => {
        return currentValue.id !== file.id
      })
      this.change()
      this.$emit('remove', file, this.imageList)
    },
    // 预览图片
    handleImagePictureCardPreview(file, index) {
      this.imageViewerVisible = true
      const imageViewFilter = this.imageList.map(
        (currentValue) => currentValue.url
      )
      const beforeList = imageViewFilter.splice(index)
      this.imageViewerList = beforeList.concat(imageViewFilter)
    },
    // 关闭图片查看器
    handleCloseimageViewerVisible() {
      this.imageViewerVisible = false
    },
    // 下载图片
    handleImageDownload(file) {
      // console.log(file)
    },
    // 上传数量限制提示
    handleImageExceed(files, fileList) {
      this.cancelSelectedNotify(
        this.$t('jspImageUpload.on_exceed_tips', { limit: this.limit })
      )
    },
    // 拖拽开始
    handleImageOnStart(e) {
      this.$emit('dragStart', e, this.imageList)
    },
    // 拖拽结束
    handleImageOnEnd(e) {
      this.change()
      this.$emit('dragEnd', e, this.imageList)
    },
    // 最终返回的处理结果：
    // 触发：上传成功、删除已上传图片、拖拽结束、图片裁剪上传成功时
    change() {
      const ids = this.imageList
        .map((currentValue) => currentValue.id)
        .join(',')
      const result = { ids, imageList: this.imageList }
      this.$emit('change', result)
    },
    // 填写图片描述
    handleDescriptionInput(value) {
      this.change()
    },
    // 添加本地图片并批量上传时执行
    handleOnChange(file, fileList) {
      if (file.status === 'ready') {
        this.validateSelectedFile(file, fileList)
      } else {
        if (fileList.length === 0) {
          this.$emit('error', this.error_response)
          this.handleSetEmpty()
          return false
        }
        if (
          this.success_uploaded_num + this.error_uploaded_num ===
          this.selected_fileList.length
        ) {
          this.success_uploaded_num > 0
            ? this.$emit('success', this.success_response, file, fileList)
            : this.$emit('error', this.error_response)
          this.handleSetEmpty()
        }
      }
    },
    handleOnRemove(file, fileList) {
      this.removeSelectedFile(file)
    },
    handleDrawerClear() {
      this.$refs['thumbnailUpload'].clearFiles()
      this.selected_fileList = []
    },
    handleSetEmpty() {
      this.$refs['thumbnailUpload'].clearFiles()
      this.selected_fileList = []
      this.submitLoading = false
      this.error_response = null
      this.success_response = null
      this.success_uploaded_num = 0
      this.error_uploaded_num = 0
      this.showUploadDrawer = false
    },
    async handleDrawerCancel() {
      if (this.submitLoading) {
        const response = await this.$confirm(
          this.$t('jspImageUpload.cancel_confirm_tips'),
          {
            confirmButtonText: this.$t('common.confirm'),
            cancelButtonText: this.$t('common.cancel'),
            type: 'warning'
          }
        ).catch(() => {})
        if (response !== 'confirm') {
          return false
        }
        this.$refs['thumbnailUpload'].abort()
      }
      this.handleSetEmpty()
      return true
    },
    async handleDrawerClose(done) {
      this.handleDrawerCancel() && done()
    },
    // 移除不合法的文件
    removeSelectedFile(file) {
      this.selected_fileList = this.selected_fileList.filter((currentValue) => {
        return currentValue.uid !== file.uid
      })
    },
    // 文件选择错误提示
    cancelSelectedNotify(message, file) {
      var h = this.$createElement
      setTimeout(() => {
        this.$notify.error({
          title: this.$t('jspImageUpload.canceled_select'),
          message: h('i', {}, [h('p', file ? file.name : ''), h('p', message)])
        })
      }, 100)
    },
    // 验证已选择的文件并移除非法文件
    validateSelectedFile(file, fileList) {
      this.selected_fileList.push(file)
      if (this.imageList.length + this.selected_fileList.length > this.limit) {
        this.removeSelectedFile(file)
        this.cancelSelectedNotify(
          this.$t('jspImageUpload.on_exceed_tips', { limit: this.limit }),
          file
        )
        return false
      }
      const ext = file.name.substring(file.name.lastIndexOf('.')).toLowerCase()
      const isFileType = this.acceptType.split(',').some((e) => {
        return e.replace(/\s+/g, '').toLowerCase() === ext
      })
      const isSize = file.size / 1024 / 1024 < this.size
      if (!isFileType || !isSize) {
        this.removeSelectedFile(file)
      }
      if (!isFileType) {
        this.cancelSelectedNotify(
          this.$t('jspImageUpload.acceptType_tips', {
            acceptType: this.accept
          }),
          file
        )
        return false
      }
      if (!isSize) {
        this.cancelSelectedNotify(
          this.$t('jspImageUpload.size_tips', { size: this.size }),
          file
        )
        return false
      }
    },
    // 自定义上传
    async handleUpload() {
      if (this.selected_fileList.length <= 0) {
        this.$notify.warning({
          title: this.$t('common.warm_tips'),
          message: this.$t('jspImageUpload.not_found')
        })
        return false
      }
      this.submitLoading = true
      this.$refs['thumbnailUpload'].submit()
    },
    // 上传响应处理
    handleOnSuccess(response, file, fileList) {
      if (response.status) {
        this.success_uploaded_num++
        response.data.items.forEach((i, index) => {
          this.imageList.push({
            id: i.id ?? '',
            name: i.name ?? '',
            description: i.description ?? '',
            path: i.path ?? '',
            url: i.url ?? ''
          })
        })
        this.success_response = response
        this.change()
      } else {
        this.error_uploaded_num++
        this.error_response = response.message
      }
    },
    // 上传失败处理
    // eslint-disable-next-line handle-callback-err
    handleOnError(error, file) {
      this.error_uploaded_num++
      this.error_response = JSON.parse(error.message)
    },
    // 图片编辑
    handleImageEdit(file, index) {
      this.cropperConfig = { img: file.url }
      if (this.width > 0 && this.height > 0) {
        this.cropperConfig['autoCropWidth'] = this.width
        this.cropperConfig['autoCropHeight'] = this.height
        this.cropperConfig['fixedNumber'] = [1, this.height / this.width]
      } else {
        this.cropperConfig['fixed'] = false
      }
      this.cropperOriginFile = file
      this.cropperVisible = true
    },
    // 完成剪切
    handleCropperCut(data) {
      this.cropperVisible = false
    },
    // 上传裁剪后的图片并替换原有图片
    async handleCropperUpload(cropper) {
      const formData = new FormData()
      formData.append(this.name, cropper.blob, cropper.origin.name)
      await request
        .post(this.upload_action, formData)
        .then((response) => {
          // 替换原有图片
          const repalce_item = response.data.items[0]
          this.imageList.forEach((item, index) => {
            if (cropper.origin.id === item.id) {
              this.imageList[index] = repalce_item
            }
          })
          this.change()
        })
        .catch(() => {
          this.$emit('error', null)
        })
      this.cropperVisible = false
    }
  }
}
</script>

<style  lang="scss"  scoped>
@import '~@/styles/variables.scss';
/* 可拖拽上传组件样式 */
.draggable-upload-group {
  /* line-height: 1.5; */
  .draggable-upload-group-show-section,
  .draggable-upload-group-show-list,
  .draggable-upload-group-uploadbar {
    display: inline;
  }
  .draggable-upload-group-uploadbar {
    .el-upload--picture-card {
      margin-bottom: 10px;
    }
  }
  .draggable-upload-group-show-section {
    .draggable-upload-group-show-list {
      margin: 0 10px 10px 0;
      display: inline-block;
      .image-unit {
        width: 148px;
        height: 148px;
        overflow: hidden;
        position: relative;
        border: 1px solid #{$border-color-base};
        border-radius: 6px;
        box-sizing: border-box;
        .el-upload-list__item-thumbnail {
          width: 100%;
          height: 100%;
        }
        .el-upload-list__item-actions {
          position: absolute;
          width: 100%;
          height: 100%;
          left: 0;
          top: 0;
          cursor: default;
          text-align: center;
          color: #fff;
          opacity: 0;
          font-size: 20px;
          background-color: rgba(0, 0, 0, 0.5);
          transition: opacity 0.3s;
          display: block;
          .el-upload-list__item-delete {
            right: 10px;
            top: 0;
            display: none;
            position: static;
            font-size: inherit;
            color: inherit;
          }
          span {
            display: none;
            cursor: pointer;
          }
          span + span {
            margin-left: 15px;
          }
          &:after {
            display: inline-block;
            content: '';
            height: 100%;
            vertical-align: middle;
          }
          &:hover {
            opacity: 1;
          }
          &:hover span {
            display: inline-block;
          }
        }
      }
    }
  }
  .picture-collection {
    display: inherit !important;
    .draggable-upload-group-show-list {
      width: 100%;
      margin: 0 0 10px 0;
      display: -webkit-flex; /* Safari */
      display: flex;
      .picture-description {
        flex: auto;
        padding-left: 10px;
        ::v-deep textarea {
          height: 148px;
        }
      }
    }
  }
  .draggable-upload-group-upload-drawer {
    ::v-deep .el-drawer__header {
      font-size: 18px;
      border-bottom: 1px solid #{$border-color-base};
      padding: 10px 20px;
      margin-bottom: 0;
    }
    ::v-deep .el-drawer__body {
      overflow-y: auto;
      .draggable-upload-group-upload-drawer-content {
        display: flex;
        flex-direction: column;
        height: 100%;
        .draggable-upload-group-upload-drawer-upload-bar {
          padding: 20px;
          flex: 1;
          overflow-y: auto;
          .el-upload {
            display: block;
          }
          .upload_tips {
            width: auto;
            margin: 20px auto;
            word-break: break-word;
          }
        }
        .draggable-upload-group-upload-drawer-submit-section {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 20px;
          border-top: 1px solid #{$border-color-base};
          .el-checkbox {
            margin-right: 20px;
          }
          .el-button-group {
          }
        }
      }
    }
  }
}

@media screen and (max-width: 680px) {
  .draggable-upload-group {
    // 上传抽屉
    ::v-deep .draggable-upload-group-upload-drawer {
      .el-drawer {
        width: 100% !important;
      }
    }
  }
}
</style>
